﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using EuroManager.Domain.Simulator.Engine;
using EuroManager.Domain.Simulator.Helpers;
using EuroManager.Domain.Simulator.World;

namespace EuroManager.Domain.Simulator.Tactics
{
    public class PlayerTactics : IPlayerTactics
    {
        public static readonly int NodesX = 6;
        public static readonly int NodesY = 4;
        public static readonly float ZoneWidth = 1.0f / (NodesX - 1);
        public static readonly float ZoneHeight = 1.0f / (NodesY - 1);

        private Vector2D kickOffLocation;
        private Dictionary<PitchNode, Vector2D> locations = new Dictionary<PitchNode, Vector2D>();

        public PlayerTactics(float kickOffX, float kickOffY)
        {
            Contract.Requires(kickOffX.IsRational());
            Contract.Requires(kickOffY.IsRational());

            this.kickOffLocation = new Vector2D(kickOffX, kickOffY);
        }

        public Vector2D CalculateKickOffLocation(IPitch pitch, PitchSide side)
        {
            return pitch.MapToMetricalLocation(side, kickOffLocation);
        }

        public Vector2D CalculateTacticalLocation(IPitch pitch, PitchSide side, Vector2D ballLocation)
        {
            ballLocation = pitch.MapToProportionalLocation(side, ballLocation);

            int zoneX = (int)Maths.Min(ballLocation.X / ZoneWidth, NodesX - 2);
            int zoneY = (int)Maths.Min(ballLocation.Y / ZoneHeight, NodesY - 2);

            Vector2D leftTop = locations[new PitchNode(zoneX, zoneY)];
            Vector2D rightTop = locations[new PitchNode(zoneX + 1, zoneY)];
            Vector2D leftBottom = locations[new PitchNode(zoneX, zoneY + 1)];
            Vector2D rightBottom = locations[new PitchNode(zoneX + 1, zoneY + 1)];

            float xRatio = (ballLocation.X - zoneX * ZoneWidth) / ZoneWidth;
            float yRatio = (ballLocation.Y - zoneY * ZoneHeight) / ZoneHeight;

            float xTop = WeightedValue(leftTop.X, rightTop.X, 1 - xRatio);
            float xBottom = WeightedValue(leftBottom.X, rightBottom.X, 1 - xRatio);
            float yLeft = WeightedValue(leftTop.Y, leftBottom.Y, 1 - yRatio);
            float yRight = WeightedValue(rightTop.Y, rightBottom.Y, 1 - yRatio);

            float x = WeightedValue(xTop, xBottom, 1 - yRatio);
            float y = WeightedValue(yLeft, yRight, 1 - xRatio);

            return pitch.MapToMetricalLocation(side, x, y);
        }

        public void Assign(int nodeX, int nodeY, float x, float y)
        {
            Contract.Requires(x.IsRational() && 0 <= x && x <= 1);
            Contract.Requires(y.IsRational() && 0 <= y && y <= 1);

            Assign(new PitchNode(nodeX, nodeY), x, y);
        }

        private void Assign(PitchNode node, float x, float y)
        {
            Contract.Requires(x.IsRational() && 0 <= x && x <= 1);
            Contract.Requires(y.IsRational() && 0 <= y && y <= 1);

            locations[node] = new Vector2D(x, y);
        }

        private float WeightedValue(float value1, float value2, float weight)
        {
            return value1 * weight + value2 * (1 - weight);
        }
    }
}
